package xyz.jcat.generator.mybatis;

import xyz.jcat.common.Const;
import xyz.jcat.common.util.ArrayUtils;
import xyz.jcat.common.util.CollectionUtils;
import xyz.jcat.common.util.ObjectUtils;
import xyz.jcat.common.util.StringUtils;
import xyz.jcat.generator.mybatis.bo.JavaField;
import xyz.jcat.generator.mybatis.bo.JavaInfo;
import xyz.jcat.generator.mybatis.config.DbConfig;
import xyz.jcat.generator.mybatis.config.FilePathConfig;
import xyz.jcat.generator.mybatis.config.PackageConfig;
import xyz.jcat.generator.mybatis.config.PersonalizedConfig;
import xyz.jcat.mybatis.DBConst;

import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;

import static xyz.jcat.common.Const.PATH_SEPARATOR;

/**
 * 生成文件前获取相应数据工具类
 *
 * @author yueyue
 * @Time 2017-05-18 14:24
 */
public class DataBuilder {

    private TableInfoDao tableInfoDao;

    /**
     * 生成类信息集合
     */
    private List<JavaInfo> javaInfoList;
    private PackageConfig packageConfig;
    private FilePathConfig filePathConfig;

    /**
     * 为javaInfoList、packageConfig、filePathConfig赋值
     *
     * @param dbConfig
     * @param personalizedConfig
     */
    public DataBuilder(DbConfig dbConfig, PersonalizedConfig personalizedConfig, TableInfoDao tableInfoDao) {
        this.tableInfoDao = tableInfoDao;
        initJavaInfos(dbConfig, personalizedConfig);
        initPackageConfig(personalizedConfig);
        initFileDir(personalizedConfig);
    }

    /**
     * 从数据库中查询到生成java文件的相关信息
     *
     * @param dbConfig
     * @param personalizedConfig
     */
    private void initJavaInfos(DbConfig dbConfig, PersonalizedConfig personalizedConfig) {
        //获取需要生成类文件的数据表名
        List<String> tableNameList = getTableNameList(dbConfig, personalizedConfig);
        boolean fieldRemoveUnderline = personalizedConfig.isFieldRemoveUnderline();
        String idType = personalizedConfig.getIdType().name();
        javaInfoList = new ArrayList<JavaInfo>();
        for (String tableName : tableNameList) {
            //查询表信息
            JavaInfo javaInfo = tableInfoDao.javaFileInfo(tableName, dbConfig);
            //获取基础类名，需判断是否需要去掉表前缀
            String className = tableName;
            String[] tablePrefixes = personalizedConfig.getTablePrefixes();
            if (ArrayUtils.isNotEmpty(tablePrefixes)) {
                for (String tablePrefix : tablePrefixes) {
                    if (StringUtils.startsWith(className, tablePrefix)) {
                        if(!StringUtils.endsWith(tablePrefix, Const.UNDERLINE)) {
                            tablePrefix = tablePrefix + Const.UNDERLINE;
                        }
                        className = className.replaceFirst(tablePrefix, Const.EMPTY);
                        break;
                    }
                }
            }
            String[] tableSuffixes = personalizedConfig.getTableSuffixes();
            if (ArrayUtils.isNotEmpty(tableSuffixes)) {
                for (String tableSuffix : tableSuffixes) {
                    if (StringUtils.endsWith(className, tableSuffix)) {
                        if(!StringUtils.startsWith(tableSuffix, Const.UNDERLINE)) {
                            tableSuffix = Const.UNDERLINE + tableSuffix;
                        }
                        className = StringUtils.replaceLast(className, tableSuffix, Const.EMPTY);
                        if(personalizedConfig.isRemoveTableSuffix()) {
                            javaInfo.setTableName(StringUtils.replaceLast(tableName, tableSuffix, Const.EMPTY));
                        }
                        break;
                    }
                }
            }
            if (fieldRemoveUnderline) {
                className = StringConvertUtils.underlineToCamel4ClassName(className);
            }
            javaInfo.setClassName(className);
            javaInfo.setPoName(String.format(personalizedConfig.getPoName(), className));
            javaInfo.setDaoName(String.format(personalizedConfig.getDaoName(), className));
            javaInfo.setMapperName(String.format(personalizedConfig.getMapperName(), className));
            javaInfo.setServiceName(String.format(personalizedConfig.getServiceName(), className));
            javaInfo.setServiceImplName(String.format(personalizedConfig.getServiceImplName(), className));
            List<JavaField> fieldsList = javaInfo.getFieldsList();
            boolean hasTableField = false;
            for (JavaField javaField : fieldsList) {
                //为java字段名赋值
                String fieldName = javaField.getDbField();
                if (fieldRemoveUnderline) {
                    fieldName = StringConvertUtils.underlineToCamel(fieldName);
                }
                javaField.setBeanField(fieldName);
                //mybatis-plus自动填充注解
                List<String> anns = new ArrayList<>(1);
                //判断该字段是否为主键
                if (javaField.isPk()) {
                    javaInfo.setPk(javaField);
                    anns.add(MessageFormat.format("@TableId(type = IdType.{0})", idType));
                    javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.TableId");
                    javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.IdType");
                }
                //TODO 通用字段字段
                if (ObjectUtils.in(fieldName,
                        DBConst.PO_FIELD_UPDATE_TIME,
                        DBConst.PO_FIELD_UPDATER)) {
                    anns.add("@TableField(fill = FieldFill.INSERT_UPDATE)");
                    javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.TableField");
                    hasTableField = true;
                } else if (ObjectUtils.in(fieldName,
                        DBConst.PO_FIELD_CREATE_TIME,
                        DBConst.PO_FIELD_CREATOR)) {
                    anns.add("@TableField(fill = FieldFill.INSERT)");
                    javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.TableField");
                    hasTableField = true;
                }
                if (!CollectionUtils.isEmpty(anns)) {
                    javaField.setAnnotations(anns);
                }
            }
            if (hasTableField) {
                if (CollectionUtils.isEmpty(javaInfo.getImportList())) {
                    javaInfo.setImportList(new HashSet<>());
                }
                javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.TableId");
                javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.IdType");
                javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.TableField");
                javaInfo.getImportList().add("com.baomidou.mybatisplus.annotation.FieldFill");
            }
            javaInfoList.add(javaInfo);
        }
    }

    /**
     * 获取java类的package
     *
     * @param personalizedConfig
     */
    private void initPackageConfig(PersonalizedConfig personalizedConfig) {
        packageConfig = new PackageConfig();
        String basePackage = personalizedConfig.getGroup() + PATH_SEPARATOR + personalizedConfig.getProjectName();
        packageConfig.setPoPackage(basePackage + PATH_SEPARATOR + personalizedConfig.getPoPackage());
        packageConfig.setDaoPackage(basePackage + PATH_SEPARATOR + personalizedConfig.getDaoPackage());
        packageConfig.setServicePackage(basePackage + PATH_SEPARATOR + personalizedConfig.getServicePackage());
        packageConfig.setServiceImplPackage(basePackage + PATH_SEPARATOR + personalizedConfig.getServiceImplPackage());
    }

    /**
     * 设置文件存放路径
     *
     * @param personalizedConfig
     */
    private void initFileDir(PersonalizedConfig personalizedConfig) {
        String saveDir = personalizedConfig.getSaveDir();
        filePathConfig = new FilePathConfig();
        filePathConfig.setPoFilePath(convertToFilePath(saveDir, packageConfig.getPoPackage()));
        filePathConfig.setDaoFilePath(convertToFilePath(saveDir, packageConfig.getDaoPackage()));
        filePathConfig.setServiceFilePath(convertToFilePath(saveDir, packageConfig.getServicePackage()));
        filePathConfig.setServiceImplFilePath(convertToFilePath(saveDir, packageConfig.getServiceImplPackage()));
        filePathConfig.setMapperXmlFilePath(personalizedConfig.getMapperXmlPath());
        mkdirs(filePathConfig.getMapperXmlFilePath());
    }


    private String convertToFilePath(String saveDir, String packageName) {
        String filePath = saveDir + File.separator + packageName.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
        mkdirs(filePath);
        return filePath;
    }

    private void mkdirs(String filePath) {
        File fileDir = new File(filePath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
    }

    private List<String> getTableNameList(DbConfig dbConfig, PersonalizedConfig personalizedConfig) {
        List<String> allTableNameList = tableInfoDao.tableList(dbConfig);
        List<String> tableNameList = new ArrayList<String>();
        if (CollectionUtils.isEmpty(allTableNameList)) {
            return tableNameList;
        }
        String[] includeTables = personalizedConfig.getIncludeTables();
        if (ArrayUtils.isNotEmpty(includeTables)) {
            for (String tableName : includeTables) {
                if (allTableNameList.contains(tableName)) {
                    tableNameList.add(tableName);
                }
            }
            return tableNameList;
        }
        String[] excludeTables = personalizedConfig.getExcludeTables();
        if (ArrayUtils.isNotEmpty(excludeTables)) {
            List<String> excludeTableList = Arrays.asList(excludeTables);
            for (String tableName : allTableNameList) {
                if (!excludeTableList.contains(tableName)) {
                    tableNameList.add(tableName);
                }
            }
            return tableNameList;
        }
        return allTableNameList;
    }


    public List<JavaInfo> getJavaInfoList() {
        return javaInfoList;
    }

    public PackageConfig getPackageConfig() {
        return packageConfig;
    }

    public FilePathConfig getFilePathConfig() {
        return filePathConfig;
    }
}
